home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Information / CSMP Digest / volume 2 / csmp-v2-011.txt < prev    next >
Text File  |  1995-06-30  |  49KB  |  1,557 lines

  1. C.S.M.P. Digest             Wed, 10 Feb 93       Volume 2 : Issue 11
  2.  
  3. Today's Topics:
  4.  
  5.     Help for a new programmer (CDEF's & Debuging Resource Code)
  6.     Proper way to do Self-Modifying code?
  7.     help- sound playing w/ bufferCmd
  8.     code for Finding Prefs File (long)
  9.  
  10.  
  11.  
  12. The Comp.Sys.Mac.Programmer Digest is moderated by Michael A. Kelly.
  13.  
  14. The digest is a collection of article threads from the internet newsgroup
  15. comp.sys.mac.programmer.  It is designed for people who read c.s.m.p. semi-
  16. regularly and want an archive of the discussions.  If you don't know what a
  17. newsgroup is, you probably don't have access to it.  Ask your systems
  18. administrator(s) for details.  If you don't have access to news, you can
  19. post articles to any newsgroup by mailing your article to
  20.     newsgroup@cs.utexas.edu
  21. So, to post an article to comp.sys.mac.programmer, mail your article to
  22.     comp-sys-mac-programmer@cs.utexas.edu
  23. Note the '-' instead of '.' in the newsgroup name.  Be sure to ask that
  24. replies be emailed to you instead of posted to the group, and give your
  25. email address.
  26.  
  27. Each issue of the digest contains one or more sets of articles (called
  28. threads), with each set corresponding to a 'discussion' of a particular
  29. subject.  The articles are not edited; all articles included in this digest
  30. are in their original posted form (as received by our news server at
  31. cs.uoregon.edu).  Article threads are not added to the digest until the last
  32. article added to the thread is at least one month old (this is to ensure that
  33. the thread is dead before adding it to the digest).  Article threads that
  34. consist of only one message are generally not included in the digest.
  35.  
  36. The entire digest is available for anonymous ftp from ftp.cs.uoregon.edu
  37. [128.223.8.8] in the directory /pub/mac/csmp-digest.  Be sure to read the
  38. file /pub/mac/csmp-digest/README before downloading any files.  The most
  39. recent issues are available from sumex-aim.stanford.edu [36.44.0.6] in the
  40. directory /info-mac/digest/csmp.  If you don't have ftp capability, the sumex
  41. archive has a mail server; send a message with the text '$MACarch help' (no
  42. quotes) to LISTSERV@ricevm1.rice.edu for more information.
  43.  
  44. The digest is also available via email.  Just send a note saying that you
  45. want to be on the digest mailing list to mkelly@cs.uoregon.edu, and you will
  46. automatically receive each new issue as it is created.  Sorry, back issues
  47. are not available through the mailing list.
  48.  
  49. Send administrative mail to mkelly@cs.uoregon.edu.
  50.  
  51.  
  52. -------------------------------------------------------
  53.  
  54. From: kevinb@engr.LaTech.edu (Kevin P. Brunner)
  55. Subject: Help for a new programmer (CDEF's & Debuging Resource Code)
  56. Date: 6 Jan 93 04:55:39 GMT
  57. Organization: Louisiana Tech University
  58.  
  59.  
  60.   I've been working on the mac for the last 6 months, recently converted from
  61. ibm p.c.  I'm getting my feet wet with controls and resources.  I have been 
  62. able to create small apps with standard mac stuff, but would like to design
  63. my own controls.  Inside Macintosh does a lot for stating generalities...
  64. "you need this, and one of these, and, oh!, one of these too..." but no 
  65. specific examples in this case.
  66.  
  67.   So I've tried creating a CDEF which responds only to the draw message and
  68. returns zero (0) for everything else.  It responds, but draws something
  69. completely different than what the code says.  The code inverts a block
  70. of pixels in a window (it works when called from the "main" block of code,
  71. but not when the resource is compiled seperately -- I get a rounded box!)
  72.  
  73.   Has anyone written custom controls?  If so, do you mind sharing a portion
  74. of your code and development technique?  Also, any help on using Think C
  75. to debug resources would be appreciated.  I have most of the Inside Mac's and
  76. both Programming the Macintosh's (vol 1 and 2)  I have looked elsewhere but
  77. have found nothing with coding examples or techniques.  Book or Article 
  78. references would be appreciated as well.
  79.  
  80. Thanks in advance!
  81.  
  82. kevinb@engr.latech.edu
  83.  
  84. +++++++++++++++++++++++++++
  85.  
  86. Organization: Royal Institute of Technology, Stockholm, Sweden
  87. Date: Wed, 6 Jan 1993 14:56:05 GMT
  88.  
  89. > How to debug CDEF in Think C
  90.  
  91. Well, you could try and create a CDEF that's only 6 bytes,
  92. load it in, make it contain a JMP instruction and the
  93. address of a function in your application, and flush the
  94. cache (after locking the resource)
  95.  
  96. Something like:
  97.  
  98. typedef struct cdef {
  99.  
  100.     unsigned short jmp ;
  101.     short * ( addr ) ( ) ;
  102.  
  103. } cdefStub ;
  104.  
  105.  
  106. pascal short
  107. MyRealCDEF ( whatever )
  108. {
  109.     Do the stuff
  110. }
  111.  
  112.  
  113. /* DO this before creating any controls */
  114.  
  115.     cdefStub stub ;
  116.     Handle h = GetReosurce ( 'CDEF' , 4711 ) ;
  117.  
  118.     stub . jmp = 0x4000 ; /* Have to look this up, JMP Immediate */
  119.     stub . addr = MyRealCDEF ;
  120.  
  121.     HLockHi ( h ) ; /* Top of heap to avoid fragmentation */
  122.  
  123.     * ( cdefStub * ) * h = stub ;
  124.  
  125.     if ( TrapAvailable ( _HWTrap ) ) {
  126.  
  127.  
  128.         FlushInstructionCache ( ) ;
  129.     }
  130.  
  131.  
  132. Now anything going to CDEF 4711 in your program will go to
  133. the function MyRealCDEF, and you can use the source debugger
  134. on it, and reference globals even! Cool!
  135.  
  136.  
  137. - -- 
  138.  -- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe --
  139.  
  140.    Cookie Jar: Vanilla Yoghurt with Crushed Oreos.
  141.  
  142. ---------------------------
  143.  
  144. From: at15+@andrew.cmu.edu (Andrew Lewis Tepper)
  145. Subject: Proper way to do Self-Modifying code?
  146. Date: 3 Jan 93 10:55:11 GMT
  147. Organization: School of Computer Science, Carnegie Mellon, Pittsburgh, PA
  148.  
  149. I'm writing a BitBlt routine that must be _very_ fast. I want to do
  150. animation of many large 256 color objects of a game. It seems the
  151. quickest way to do this would be to take an object before the animation
  152. starts, and create a sequence of move instructions that would write to
  153. just those addresses I want. To draw the object I would just load a
  154. register with the apropriate starting address and call it's draw routine.
  155.  
  156. Is there an accepted way to do this on the Mac?  I know that
  157. self-modifying code can cause problems on chips with caches, so I want
  158. to be as "kosher" as possible.
  159.  
  160. Thanks,
  161. Andy
  162.  
  163. +++++++++++++++++++++++++++
  164.  
  165. Date: 4 Jan 93 12:03:53 GMT
  166. Organization: Royal Institute of Technology, Stockholm, Sweden
  167.  
  168. In <wfFp_jO00WB707kGgq@andrew.cmu.edu> at15+@andrew.cmu.edu (Andrew Lewis Tepper) writes:
  169.  
  170. >Is there an accepted way to do this on the Mac?  I know that
  171. >self-modifying code can cause problems on chips with caches, so I want
  172. >to be as "kosher" as possible.
  173.  
  174.     if ( TrapAvailable ( _HwPriv ) ) {
  175.  
  176.         /* FIC also calls FlushDataCache */
  177.         FlushInstructionCache ( ) ;
  178.     }
  179.  
  180. Of course you'll want to put the result of TrapAvailable in
  181. a global at startup. Not to mention first modifying ALL the
  182. figures, then flushing the cache ONCE before starting to
  183. call the code.
  184.  
  185. However, I think you could write the code quite conflict-free
  186. and as fast using just address-register-relative addressing
  187. and maybe a data register for rowBytes. Modifying the instructions
  188. data takes time to do; I think using a register pair would be
  189. FASTER - and more compatible.
  190.  
  191. Cheers,
  192.  
  193.                             / h+
  194.  
  195. - -- 
  196.  -- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe --
  197.  
  198.  -- I don't fear death, it's dying that scares me.
  199.  
  200. +++++++++++++++++++++++++++
  201.  
  202. From: alexr@apple.com (Alexander M. Rosenberg)
  203. Date: 5 Jan 93 18:33:37 GMT
  204. Organization: Hackers Anonymous
  205.  
  206. In article <1993Jan4.120353.16699@kth.se> Jon Wltte, d88-jwa@dront.nada.kth.se
  207. writes:
  208. > However, I think you could write the code quite conflict-free
  209. > and as fast using just address-register-relative addressing
  210. > and maybe a data register for rowBytes. Modifying the instructions
  211. > data takes time to do; I think using a register pair would be
  212. > FASTER - and more compatible.
  213.  
  214. Immediate moved bitmaps are _much_ faster than those drawn via table lookups.
  215. This is a technique that they call "Compiled Bitmaps" in rec.games.programmer.
  216. Immediate move instructions are generally faster than using an address
  217. register. It adds up, especially when lots of small bitmaps may be necessary.
  218.  
  219. The one shot compile may even be possible to do at compile time, depending on
  220. the way the screen bitmap is available on the target machine. The problem with
  221. compiled bitmaps is that they typically are double in size over the plan data,
  222. which is why Andy would want to compile them when the game is launched.
  223. Several games take a long time to start up, like Maelstrom (although this is
  224. probably because Maelstrom keeps all it's sprite data as icl8 resources, and
  225. loading, locking, and detaching that many resources takes time).
  226. - ---------------------------------------------------------------------------
  227. - -  Alexander M. Rosenberg  - INTERNET: alexr@apple.com      - Yoyodyne    -
  228. - -  330 Waverley St., Apt B - UUCP:ucbvax!apple!alexr        - Propulsion  -
  229. - -  Palo Alto, CA 94301     -                                - Systems     -
  230. - -  (415) 329-8463          - Nobody is my employer so       - :-)         -
  231. - -  (408) 974-3110          - nobody cares what I say.       -             -
  232.  
  233. +++++++++++++++++++++++++++
  234.  
  235. Date: 5 Jan 93 20:25:43 GMT
  236. Organization: Royal Institute of Technology, Stockholm, Sweden
  237.  
  238. In <1993Jan5.183337.20064@gallant.apple.com> alexr@apple.com (Alexander M. Rosenberg) writes:
  239.  
  240. >Immediate move instructions are generally faster than using an address
  241. >register. It adds up, especially when lots of small bitmaps may be necessary.
  242.  
  243. Hmm, well, I'm not convinced. Let's say you move a bitmap. Using an
  244. address register, you will only have to change the register. For a
  245. "compiled" bitmap, you would have to change each and every immediate
  246. move instruction. Not to mention that immediate instructions and
  247. longer, and thus takes more bus cycles in fetch; those bus cycles
  248. do also compete with the cycles spent writing to VRAM. (Some slow
  249. NuBus cards take 7 cycles for a longword write, but then fetching
  250. an 8-byte instruction instead of a 4-byte one takes 4 extra cycles
  251. on an LC)
  252.  
  253. It all comes out in amortized performance; if the blitting has to be
  254. raving fast, but you have ample of time to precompute the next
  255. image, compiled bitmaps would be OK (except they flush the cache on
  256. 040s; that's bad speed-wise) but if you have general throughput
  257. problems, register-relative blitting is DEFINATELY the right thing
  258. to do.
  259.  
  260. Just pass rowBytes in d0 and the address of the first pixel in
  261. a0, and make VERY SURE that address is longword aligned or you
  262. will suffer much more from misaligned writes than you will from
  263. the difference in register-based and immediate addressing.
  264.     
  265. >The one shot compile may even be possible to do at compile time, depending on
  266. >the way the screen bitmap is available on the target machine. The problem with
  267.  
  268. Not on the Mac, of course. However, certain other systems have
  269. a measly register set and the screen card (note the singularis)
  270. on a fixed address; I'm sure the timings you cite are for VGA
  271. and not for anything flexible like the mac QD device model.
  272.  
  273. Cheers,
  274.  
  275.                             / h+
  276.  
  277. - -- 
  278.  -- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe --
  279.    This signature is kept shorter than 4 lines in the interests of UseNet
  280.    S/N ratio.
  281.  
  282. +++++++++++++++++++++++++++
  283.  
  284. From: kent@lloyd.Camex.COM (Kent Borg)
  285. Date: 5 Jan 93 21:31:38 GMT
  286. Organization: Camex Inc., Boston MA
  287.  
  288. In article <1993Jan4.120353.16699@kth.se> d88-jwa@dront.nada.kth.se (Jon Wdtte) writes:
  289. >In <wfFp_jO00WB707kGgq@andrew.cmu.edu> at15+@andrew.cmu.edu (Andrew Lewis Tepper) writes:
  290. >
  291. >>Is there an accepted way to do this on the Mac?  I know that
  292. >>self-modifying code can cause problems on chips with caches, so I want
  293. >>to be as "kosher" as possible.
  294.  
  295. I hate being non-Kosher, but just today some of my code was modifying
  296. itself.  (How can a proc in the sleep queue find my A5 world anyway?)
  297.  
  298. My "to do" list said I should check before I trap, so I added:
  299.  
  300. >    if ( TrapAvailable ( _HwPriv ) ) {
  301. >
  302. >        /* FIC also calls FlushDataCache */
  303. >        FlushInstructionCache ( ) ;
  304. >    }
  305.  
  306. It didn't work.  
  307.  
  308. At least with my .h-files, the spelling is "HWTrap".  (And now I need
  309. to type in the source for TrapAvailable()--now I remember why I didn't
  310. do this before.)
  311.  
  312. Yes, trivial, so instead consider my post as follows: How should one
  313. find one's A5 world from within a sleep queue procedure?
  314.  
  315.  
  316. - --
  317. Kent Borg            kent@camex.com or kentborg@aol.com (when it is *working*)
  318.                                             H:(617) 776-6899  W:(617) 426-3577
  319. As always, things look better when some costs are left out.
  320.                               -Economist 3-28-92 p. 94
  321.  
  322. +++++++++++++++++++++++++++
  323.  
  324. From: neeri@iis.ethz.ch (Matthias Neeracher)
  325. Date: 7 Jan 93 22:23:29 GMT
  326. Organization: Integrated Systems Laboratory, ETH, Zurich
  327.  
  328. In article <1993Jan05.163138.20910@lloyd.Camex.COM>, kent@lloyd.Camex.COM (Kent Borg) writes:
  329. > Yes, trivial, so instead consider my post as follows: How should one
  330. > find one's A5 world from within a sleep queue procedure?
  331.  
  332. I have never written a sleep queue proc, but wouldn't the following work ?
  333.  
  334. TYPE TheBigSleep =
  335.    RECORD
  336.       sleepy :        SleepQRec;
  337.       wonderfulWorld: A5WorldPtr; /* Whatever you want */
  338.    END;
  339. ...
  340. valium : TheBigSleep;
  341. ...
  342. SleepQInstall(@valium);
  343.  
  344. According to TFM, your sleep routine will be called with a pointer to
  345. valium in A0 that it can cast to the appropriate type. Am I missing anything ?
  346.  
  347. Matthias
  348.  
  349. - -----
  350. Matthias Neeracher                                      neeri@iis.ethz.ch
  351.   "I wouldn't recommend sex, drugs or insanity for everyone, but
  352.    they've always worked for me." -- Hunter S. Thompson
  353.  
  354. +++++++++++++++++++++++++++
  355.  
  356. From: kent@lloyd.Camex.COM (Kent Borg)
  357. Date: 8 Jan 93 17:50:14 GMT
  358. Organization: Camex Inc., Boston MA
  359.  
  360. In article <NEERI.93Jan7232329@iis.ethz.ch> neeri@iis.ethz.ch (Matthias Neeracher) writes:
  361. >In article <1993Jan05.163138.20910@lloyd.Camex.COM>, kent@lloyd.Camex.COM (Kent Borg) writes:
  362. >> Yes, trivial, so instead consider my post as follows: How should one
  363. >> find one's A5 world from within a sleep queue procedure?
  364. >
  365. >I have never written a sleep queue proc, but wouldn't the following work ?
  366. >
  367. >TYPE TheBigSleep =
  368. >   RECORD
  369. >      sleepy :        SleepQRec;
  370. >      wonderfulWorld: A5WorldPtr; /* Whatever you want */
  371. >   END;
  372. >...
  373. >valium : TheBigSleep;
  374. >...
  375. >SleepQInstall(@valium);
  376. >
  377. >According to TFM, your sleep routine will be called with a pointer to
  378. >valium in A0 that it can cast to the appropriate type. Am I missing anything ?
  379.  
  380. No, you are not missing anything, I was the one missing.  Sure looks
  381. like that will work.
  382.  
  383. Um, though a healthy dose of "RTFM" is appropiate here, I think there
  384. is also a little of another effect: "It is always in the *first* place
  385. you look, you just weren't looking carefully enough when you looked
  386. there because you didn't yet know how lost it was."
  387.  
  388. I spotted the A0 note, realized that it was not a pointer to my A5,
  389. nor was it a refcon, so I looked further.  By the time I realized that
  390. there was no A5 or refcon sitting waiting for me I had forgotten that
  391. I might use A0 to *find* one.  Stupid I am sometimes.
  392.  
  393. Thanks for the help.
  394.  
  395. - --
  396. Kent Borg            kent@camex.com or kentborg@aol.com (when it is *working*)
  397.                                             H:(617) 776-6899  W:(617) 426-3577
  398. As always, things look better when some costs are left out.
  399.                               -Economist 3-28-92 p. 94
  400.  
  401. ---------------------------
  402.  
  403. From: tmorrow@oracle.com (Thomas Morrow)
  404. Subject: help- sound playing w/ bufferCmd
  405. Date: 2 Jan 93 23:19:12 GMT
  406. Organization: Oracle Corporation, Belmont, CA
  407.  
  408.  
  409. I am having trouble using SndDoCommand to play a sampled sound header.
  410.  
  411. I do:
  412.     SndCommand mySndCmd;
  413.     
  414.     /* mySndH is a handle to a sound header and data */
  415.     /* mySndChan is already properly opened */
  416.     mySndCmd.cmd;
  417.     mySndCmd.param1 = 0;
  418.     mySndCmd.param2 = (long)(*mySndH) & 0xffffff;
  419.     SndDoCommand(mySndChan, mySndCmd, false);
  420.  
  421. This does not work for some reason; I can't figure out why because if
  422. I replace the SndDoCommand with a Snd Play, it works:
  423.  
  424.     SndPlay(mySndChan, mySndH, true);
  425.     
  426. So the channel and header must be alright.  I am new to Mac
  427. programming, and wasn't sure exactly how to set param2 (a long) to the
  428. header address (Ptr)... The IM IV uses ORD4() to do the type
  429. conversion, but I am using Think C rather than Lisa Pascal. My way
  430. seems to work, but what is the elegant way?
  431.  
  432. Anyway, I use SndChannelStatus to get the status every event loop
  433. cycle, and the SCStatus returned is blank (zero) in all except CPUload
  434. (24),  ChannelAttributes (initMono), and ChannelBusy (true).  How can
  435. the channel be busy if StartTime == endTime == 0?
  436.  
  437.  
  438. Please email responses.  I will summarize.
  439.  
  440.             Tom             tmorrow@us.oracle.com
  441.  
  442. - --
  443.  
  444. - ---------------------------------------------------------------------------
  445. Tom Morrow       3OP4     voice:415-506-2202    Office: 427
  446. tmorrow@us.oracle.com       fax:415-506-7292    Application Object Library
  447. - ---------------------------------------------------------------------------
  448.  
  449. +++++++++++++++++++++++++++
  450.  
  451. From: REEKES@applelink.apple.com (Jim Reekes)
  452. Date: Wed, 6 Jan 1993 00:36:46 GMT
  453. Organization: Apple Computer, Inc.
  454.  
  455. In article <TMORROW.93Jan2151912@af4hp.oracle.com>, tmorrow@oracle.com
  456. (Thomas Morrow) wrote:
  457. > I am having trouble using SndDoCommand to play a sampled sound header.
  458. > I do:
  459. >     SndCommand mySndCmd;
  460. >     
  461. >     /* mySndH is a handle to a sound header and data */
  462. >     /* mySndChan is already properly opened */
  463. >     mySndCmd.cmd;
  464. >     mySndCmd.param1 = 0;
  465. >     mySndCmd.param2 = (long)(*mySndH) & 0xffffff;
  466. >     SndDoCommand(mySndChan, mySndCmd, false);
  467. > This does not work for some reason; I can't figure out why because if
  468. > I replace the SndDoCommand with a Snd Play, it works:
  469. >     SndPlay(mySndChan, mySndH, true);
  470. >     
  471. > So the channel and header must be alright.  I am new to Mac
  472. > programming, and wasn't sure exactly how to set param2 (a long) to the
  473. > header address (Ptr)... The IM IV uses ORD4() to do the type
  474. > conversion, but I am using Think C rather than Lisa Pascal. My way
  475. > seems to work, but what is the elegant way?
  476. > Anyway, I use SndChannelStatus to get the status every event loop
  477. > cycle, and the SCStatus returned is blank (zero) in all except CPUload
  478. > (24),  ChannelAttributes (initMono), and ChannelBusy (true).  How can
  479. > the channel be busy if StartTime == endTime == 0?
  480.  
  481. You cannot use the 'snd ' resource for the bufferCmd. You must point to the
  482. SoundHeader within the resource.  Also, the code above didn't set the cmd
  483. to bufferCmd. Here's some code anyone can use to find the sound header in a
  484. 'snd ' resource. Then change your code above into...
  485.  
  486.     mySndCmd.cmd = bufferCmd;
  487.     mySndCmd.param1 = 0;
  488.     mySndCmd.param2 = (long) *mySndH + offset;
  489.     err = SndDoCommand(mySndChan, &mySndCmd, true);
  490.  
  491.  
  492. // find the sound header inside of the given snd handle
  493.  
  494. OSErr GetBufferOffset(Handle sndHandle, long *offset)
  495. {
  496.     short       howManyCmds;
  497.     Ptr         cruisePtr;
  498.     OSErr       result;
  499.  
  500.     if (sndHandle == nil)
  501.         return nilHandleErr;
  502.         
  503.     if (*sndHandle == nil)
  504.         return nilHandleErr;
  505.  
  506.     result = noErr;
  507.     *offset = 0;
  508.  
  509.     // set the pointer past the first two words of the snd
  510.     // this is correct for both format 1 and 2 resources
  511.     cruisePtr = *sndHandle + offsetof(SndListResource, modifierPart);
  512.  
  513.     // if it's a format 1, then point past the modifier parts
  514.     if ( ((SndListPtr)*sndHandle)->format == firstSoundFormat )
  515.         cruisePtr += sizeof(ModRef) *
  516. ((SndListPtr)*sndHandle)->numModifiers;
  517.  
  518.     // now pointing at number of cmds
  519.     howManyCmds = *((short *)cruisePtr);
  520.     cruisePtr += sizeof(howManyCmds);
  521.  
  522.     // cruisePtr is now at the first sound command
  523.     // cruise all commands and find a soundCmd or bufferCmd
  524.     do {
  525.         switch (((SndCmdPtr)cruisePtr)->cmd) {
  526.  
  527.             case (soundCmd | dataOffsetFlag):
  528.             case (bufferCmd | dataOffsetFlag):
  529.                 *offset = ((SndCmdPtr)cruisePtr)->param2;
  530.                 howManyCmds = 0;                // done, get out of loop
  531.                 break;
  532.  
  533.             default:                            // catch any other type of
  534. cmd
  535.                 cruisePtr += sizeof(SndCommand);
  536.                 howManyCmds -= 1;
  537.                 break;
  538.         }
  539.     } while (howManyCmds >= 1);                 // done with all the
  540. commands
  541.  
  542.     if (*offset == 0)                           // never found sound header
  543.         return badFormat;
  544.         
  545.     return(result);
  546. }
  547.  
  548.  
  549.  
  550. - -----------------------------------------------------------------------
  551. Jim Reekes, Polterzeitgeist  |     Macintosh Toolbox Engineering
  552.                              |          Sound Manager Expert
  553. Apple Computer, Inc.         | "All opinions expressed are mine, and do
  554. 20525 Mariani Ave. MS: 81-KS |   not necessarily represent those of my
  555. Cupertino, CA 95014          |       employer, Apple Computer Inc."
  556.  
  557. ---------------------------
  558.  
  559. From: Sproul@sproul.sproul.com (Mark Sproul)
  560. Subject: code for Finding Prefs File (long)
  561. Date: 5 Jan 93 18:15:16 GMT
  562. Organization: Sproul Consulting
  563.  
  564. Below is code for finding and working with prefs file in system 7
  565. prefernce folder and working with said file
  566.  
  567. There are people looking for this code again, I posted it before.
  568. Since I posted it last, I have made some additions to it.  This code
  569. is originally from Inside Mac Comm ToolBox.
  570.  
  571. ============================================================================
  572. FILE: doPrefs.proto.h
  573. ============================================================================
  574.  
  575. void        getPrefsFile(Str255, OSType, OSType);
  576. Boolean        getPrefsResourceStr(Str255, OSType, Str255);
  577. Boolean        getPrefsResourceData(Str255, OSType, char *, long);
  578. void        setPrefsResourceStr(Str255, OSType, Ptr, long);
  579. void        setCTBpref(Str255);
  580. void        DoCommPortSetup(Str255);
  581.  
  582. ============================================================================
  583. FILE: doPrefs.c
  584. ============================================================================
  585.  
  586.  
  587. /******************************************************************************
  588. * adapted from
  589. *  Inside the Macintosh Communications Toolbox
  590. * Page  333
  591. * After initialization, the code shown first checks if a
  592. * preferences folder, which contains tool settings written in preference
  593. * files, already exists. If so, the application uses the settings in this file.
  594. * Otherwise, the code generates a new preferences file.
  595. ******************************************************************************
  596. * Modifications
  597. ******************************************************************************
  598. * Nov 27, 1991    Original version had a bug in it.
  599. *                If the "Preferences" directory already existed, the preferences file
  600. *                was placed in the system folder, not in the "Preferences" directory.
  601. *                If the directory did NOT exist, everything worked fine.
  602. *                I also changed it to check for the existance of the "Preferences" folder
  603. *                first instead of trying to create one and letting an duplicate file error
  604. *                indicate that it already existed.
  605. *                by Mark Sproul
  606. *                AppleLink: Sproul.M
  607. * Jan 16, 1992    Working on a general purpose prefs file manipulator
  608. * Jan 25, 1992    Prefs file resources working great
  609. ******************************************************************************/
  610.  
  611. #include    <Connections.h>
  612. #include    <CommResources.h>
  613. #include    "doPrefs.proto.h"
  614. /*
  615. ** Global Variables used by all of the prefs routines
  616. */
  617. long            prefDirID;            /* Prefs Dir ID number */
  618. long            sysfDirID;            /* System folder Dir ID number */
  619. short            prefVRefNum;        /* Prefs Volume Ref number */
  620. short            prefFileOKFlag = 0;    /* prefs file OK indicator */
  621.  
  622. /*************************************************************
  623. * This makes sure there is a prefs file in the PREFERENCES directory
  624. * if not, it creates it.
  625. *
  626. * getPrefsFile MUST be called before any atempt at getting the prefs values
  627. *
  628. *************************************************************/
  629.  
  630. void getPrefsFile(prefsFileName, creatorType, fileType)
  631. Str255        prefsFileName;
  632. OSType        creatorType;
  633. OSType        fileType;
  634. {
  635. OSErr            osErr        = noErr;
  636. SysEnvRec        theWorld;
  637. CInfoPBPtr        infoPB;
  638. WDPBPtr            wdPB;
  639. HParmBlkPtr        dirPB;
  640.  
  641. short            prefRefNum;
  642. Point            where    = { 75, 75 };
  643. Str63            toolName;
  644. short            procID;
  645. Handle            h;
  646. Ptr                p;
  647.  
  648.  
  649. ConnHandle        prefConn;
  650.  
  651. ConnHandle        docConn;
  652. CMBufferSizes    sizes    = { 0, 0, 0, 0, 0, 0, 0, 0 };
  653.  
  654.  
  655.  
  656.     infoPB        = (CInfoPBPtr)NewPtrClear(sizeof(*infoPB));
  657.     wdPB        = (WDPBPtr)NewPtrClear(sizeof(*wdPB));
  658.     dirPB        = (HParmBlkPtr)NewPtrClear(sizeof(*dirPB));
  659.  
  660.  
  661.     /* find the system folder's volume reference number and directory ID */
  662.     osErr = SysEnvirons(curSysEnvVers, &theWorld);
  663.     (*wdPB).ioVRefNum = theWorld.sysVRefNum;
  664.  
  665.     if (noErr == (osErr = PBGetWDInfo(wdPB, false)))
  666.     {
  667.         /*********************************************************
  668.         * 11-27-91 Modified by Mark Sproul
  669.         **********************************************************/
  670.         /* get the Volume Reference Number and save it */
  671.         prefVRefNum    = (*wdPB).ioWDVRefNum;
  672.         /* get the System directory ID and save it */
  673.         sysfDirID    = (*wdPB).ioWDDirID;
  674.         
  675.         /* check for the preferences folder */
  676.         (*infoPB).hFileInfo.ioFDirIndex    = 0;
  677.         (*infoPB).hFileInfo.ioVRefNum    = prefVRefNum;
  678.         (*infoPB).hFileInfo.ioDirID        = sysfDirID;
  679.         (*infoPB).hFileInfo.ioNamePtr    = "\pPreferences";
  680.         osErr = PBGetCatInfo(infoPB, false);
  681.         /* save the "Preferecnces" dir number */ 
  682.         prefDirID    = (*infoPB).hFileInfo.ioDirID;
  683.         
  684.         if (osErr == fnfErr)
  685.         {
  686.             /* Create "Preferences" folder */
  687.             (*dirPB).fileParam.ioVRefNum    = prefVRefNum;
  688.             (*dirPB).fileParam.ioDirID        = sysfDirID;
  689.             (*dirPB).fileParam.ioNamePtr    = "\pPreferences";
  690.             osErr = PBDirCreate(dirPB, false);
  691.             prefDirID    = (*dirPB).fileParam.ioDirID;
  692.         }
  693.  
  694.         /*********************************************************
  695.         * end of modifications
  696.         **********************************************************/
  697.         if (osErr == noErr)
  698.         {
  699.             /* does the preference file exist? */
  700.             (*infoPB).hFileInfo.ioFDirIndex    = 0;
  701.             (*infoPB).hFileInfo.ioVRefNum    = prefVRefNum;
  702.             (*infoPB).hFileInfo.ioDirID        = prefDirID;
  703.             (*infoPB).hFileInfo.ioNamePtr    = prefsFileName;
  704.             osErr = PBGetCatInfo(infoPB, false);
  705.             if (osErr == noErr)
  706.             {
  707.                 /* set flag saying the prefs file is OK */
  708.                 prefFileOKFlag = 0xAA;
  709.             }
  710.             if (osErr == fnfErr)
  711.             {
  712.                 /* no, so create a new preference file */
  713.                 if (noErr == (osErr = HCreate(prefVRefNum, prefDirID, prefsFileName, creatorType, fileType)))
  714.                 {
  715.                     HCreateResFile(prefVRefNum, prefDirID, prefsFileName);
  716.                     if (noErr == (osErr = ResError()))
  717.                     {
  718.                         /* open the preference file */
  719.                         prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
  720.                         if (prefRefNum == -1)
  721.                         {
  722.                             osErr = ResError();
  723.                         }
  724.                         else
  725.                         {
  726.                             /* create a default connection */
  727.                             osErr = CRMGetIndToolName(classCM, 1,toolName);
  728.                             if (noErr == osErr)
  729.                             {
  730.                                 prefConn = CMNew(CMGetProcID(toolName), cmData, sizes, 0, 0);
  731.                                 /* leave the default setting as the preferance */
  732.                                 
  733.                                 /************************************************************
  734.                                 * ORIGINALLY, the code let the user select setup at this point
  735.                                 * I do not want it asking for serial port prefs on startup
  736.                                 * if they are not set.
  737.                                 * ---allow the user to select a prefered tool and configuration
  738.                                 * ---osErr = CMChoose(&prefConn, where, nil);
  739.                                 ************************************************************/
  740.                             
  741.                                 /* write the prefered tool name to the preference file */
  742.                                 HLock((Handle) prefConn);
  743.                                 CMGetToolName((**prefConn).procID, toolName);
  744.                                 HUnlock((Handle) prefConn);
  745.                                 h = NewHandle(1 + toolName[0]);
  746.                                 HLock(h);
  747.                                 BlockMove(toolName, *h, GetHandleSize(h));
  748.                                 HUnlock(h);
  749.                                 AddResource(h, 'pTXT', 0, "");
  750.                                 ReleaseResource(h);
  751.                                 /* write the prefered configuration to the preference file */
  752.                                 p = CMGetConfig(prefConn);
  753.                                 h = NewHandle(GetPtrSize(p));
  754.                                 HLock(h);
  755.                                 BlockMove(p, *h, GetHandleSize(h));
  756.                                 HUnlock(h);
  757.                                 AddResource(h, 'cTXT', 0, "");
  758.                                 ReleaseResource(h);
  759.                                 DisposPtr(p);
  760.                                 
  761.                                 /* dispose of the connection */
  762.                                 CMDispose(prefConn);
  763.                             }
  764.                             /* close the file so that it can be used in a shared environment */
  765.                             CloseResFile(prefRefNum);
  766.                             /* set flag saying the prefs file is OK */
  767.                             prefFileOKFlag = 0xAA;
  768.                         }
  769.                     }
  770.                 }
  771.             }
  772.         }
  773.     }
  774. }
  775.  
  776.  
  777.  
  778. //**************************************
  779. //* get resource String from Prefs file
  780. //*
  781. //***************************************
  782. Boolean    getPrefsResourceStr(prefsFileName, perfsResType, returnString)
  783. Str255        prefsFileName;
  784. OSType        perfsResType;
  785. Str255        returnString;
  786. {
  787. short            prefRefNum;
  788. Handle            h;
  789. int                i;
  790. Size            hSize;
  791. Boolean            returnFlag;
  792.  
  793.     h = nil;
  794.     returnFlag    = false;
  795.     /* did the prefs file get opened or created OK */
  796.     if (prefFileOKFlag == 0xAA)
  797.     {
  798.         /* focus on the preference file */
  799.         prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
  800.         if (prefRefNum != -1)
  801.         {
  802.             h = Get1Resource(perfsResType, 0);
  803.             if (h != nil)
  804.             {
  805.                 hSize = GetHandleSize(h);            /* get the size of the handle */
  806.                 if (hSize > 255) hSize = 255;
  807.                 HLock(h);
  808.                 /* had to have this to make it compile under THINK C 5.0 */
  809.                 for (i=0; i< hSize; i++)
  810.                 {
  811.                     returnString[i] = *(*h+i);
  812.                 }
  813.                 HUnlock(h);
  814.                 ReleaseResource(h);
  815.                 returnFlag    = true;            //* prefs string is OK
  816.             }
  817.             CloseResFile(prefRefNum);
  818.         }
  819.     }
  820.     else
  821.     {
  822.         //* generate prefs file alert message
  823.     }
  824.     return(returnFlag);
  825. }
  826.  
  827. //**************************************
  828. //* get resource String from Prefs file
  829. //*
  830. //***************************************
  831. Boolean    getPrefsResourceData(    Str255        prefsFileName,
  832.                                 OSType        perfsResType,
  833.                                 char        *returnString,
  834.                                 long        maxLen)
  835. {
  836. short            prefRefNum;
  837. Handle            h;
  838. int                i;
  839. Size            hSize;
  840. Boolean            returnFlag;
  841.  
  842.     h = nil;
  843.     returnFlag    = false;
  844.     /* did the prefs file get opened or created OK */
  845.     if (prefFileOKFlag == 0xAA)
  846.     {
  847.         /* focus on the preference file */
  848.         prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
  849.         if (prefRefNum != -1)
  850.         {
  851.             h = Get1Resource(perfsResType, 0);
  852.             if (h != nil)
  853.             {
  854.                 hSize = GetHandleSize(h);            /* get the size of the handle */
  855.                 if (hSize > maxLen) hSize = maxLen;
  856.                 HLock(h);
  857.                 /* had to have this to make it compile under THINK C 5.0 */
  858.                 for (i=0; i< hSize; i++)
  859.                 {
  860.                     returnString[i] = *(*h+i);
  861.                 }
  862.                 HUnlock(h);
  863.                 ReleaseResource(h);
  864.                 returnFlag    = true;            //* prefs string is OK
  865.             }
  866.             CloseResFile(prefRefNum);
  867.         }
  868.     }
  869.     else
  870.     {
  871.         //* generate prefs file alert message
  872.     }
  873.     return(returnFlag);
  874. }
  875.  
  876. //**************************************
  877. //* save resource to Prefs file
  878. //*
  879. //***************************************
  880. void    setPrefsResourceStr(Str255 prefsFileName, OSType perfsResType,char *buffer, long bufLen)
  881. {
  882. short            prefRefNum;
  883. Handle            h;
  884. int                i;
  885. Size            hSize, newSize;
  886.  
  887.     h = nil;
  888.     /* did the prefs file get opened or created OK */
  889.     if (prefFileOKFlag == 0xAA)
  890.     {
  891.         /* focus on the preference file */
  892.         prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
  893.         if (prefRefNum != -1)
  894.         {
  895.             //* get the specified resource */
  896.             h = Get1Resource(perfsResType, 0);
  897.             if (h == nil)
  898.             {
  899.                 /* resource did not exist, add it to resource file */
  900.                 h = NewHandle(1 + bufLen);
  901.                 HLock(h);
  902.                 BlockMove(buffer, *h, GetHandleSize(h));
  903.                 HUnlock(h);
  904.                 AddResource(h, perfsResType, 0, "\p");
  905.             }
  906.             else
  907.             {
  908.                 /* resoure DOES exist, change it */
  909.                 hSize = GetHandleSize(h);            /* get the size of the handle */
  910.                 /* check for size of handle */
  911.                 if (hSize != (1 + bufLen))
  912.                 {
  913.                     newSize = 1 + bufLen;
  914.                     SetHandleSize(h, newSize);
  915.                 }
  916.                 HLock(h);
  917.                 BlockMove(buffer, *h, GetHandleSize(h));
  918.                 HUnlock(h);
  919.                 ChangedResource(h);
  920.             }
  921.             ReleaseResource(h);
  922.             CloseResFile(prefRefNum);
  923.         }
  924.     }
  925.     else
  926.     {
  927.         PutAlertMessage("\pProgram cant find prefs file");
  928.     }
  929. }
  930.  
  931.  
  932. /**************************************
  933. * set Comm Tool Box connection preference
  934. *
  935. ****************************************/
  936. void    setCTBpref(prefsFileName)
  937. Str255        prefsFileName;
  938. {
  939. short            prefRefNum;
  940. OSErr            osErr, cmChooseReturnCode;
  941. int                i;
  942. Str255            prefStr;
  943. short            procID;
  944. Str63            toolName;
  945. Handle            h;
  946. Ptr                p;
  947. short            iErr;
  948. Size            hSize, newSize;
  949.  
  950. ConnHandle        docConn;
  951. CMBufferSizes    sizes    = { 0, 0, 0, 0, 0, 0, 0, 0 };
  952. Point            where    = { 40, 40 };
  953.  
  954.     if (isCTBavailable())
  955.     {
  956.         /* did the prefs file get opened or created OK */
  957.         if (prefFileOKFlag == 0xAA)
  958.         {
  959.             getPrefsResourceStr(prefsFileName,'pTXT', prefStr);
  960.     
  961.             procID = CMGetProcID(prefStr);
  962.             if (procID != -1)
  963.             {
  964.                 /* create a new connection */
  965.                 docConn    = CMNew(procID, cmData, sizes, 0, 0);
  966.                 
  967.                 if (docConn != nil)
  968.                 {
  969.                     /* set the prefered configuration */
  970.         
  971.                     getPrefsResourceStr(prefsFileName,'cTXT', prefStr);
  972.                     osErr    = CMSetConfig(docConn, (char *)prefStr);
  973.     
  974.                     cmChooseReturnCode    = CMChoose(&docConn, where, nil);
  975.                 }
  976.             }
  977.             else
  978.             {
  979.                 /* the prefered tool could not be found so I */
  980.                 osErr    = CRMGetIndToolName(classCM, 1, toolName);
  981.                 docConn    = CMNew(CMGetProcID(toolName), cmData, sizes, 0, 0);
  982.                 if (docConn != nil)
  983.                 {
  984.                     cmChooseReturnCode    = CMChoose(&docConn, where, nil);
  985.                 }
  986.             }
  987.             
  988.             if ((cmChooseReturnCode == chooseOKMinor) || (cmChooseReturnCode == chooseOKMajor))
  989.             {
  990.                 /* change the prefs file */
  991.                 
  992.                 if (prefFileOKFlag == 0xAA)
  993.                 {
  994.                     /* open the preference file */
  995.                     prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
  996.                     if (prefRefNum != -1)
  997.                     {
  998.                         /* write the prefered tool name to the preference file */
  999.                         HLock((Handle) docConn);
  1000.                         CMGetToolName((**docConn).procID, toolName);
  1001.                         HUnlock((Handle) docConn);
  1002.                         
  1003.                         /* get the port TeXT resource */
  1004.                         h = Get1Resource('pTXT', 0);
  1005.                         if (h == nil)
  1006.                         {
  1007.                             /* resource did not exist, add it to resource file */
  1008.                             h = NewHandle(1 + toolName[0]);
  1009.                             HLock(h);
  1010.                             BlockMove(toolName, *h, GetHandleSize(h));
  1011.                             HUnlock(h);
  1012.                             AddResource(h, 'pTXT', 0, "");
  1013.                         }
  1014.                         else
  1015.                         {
  1016.                             /* resoure DOES exist, change it */
  1017.                             hSize = GetHandleSize(h);            /* get the size of the handle */
  1018.                             /* check for size of handle */
  1019.                             if (hSize != (1 + toolName[0]))
  1020.                             {
  1021.                                 newSize = 1 + toolName[0];
  1022.                                 SetHandleSize(h, newSize);
  1023.                             }
  1024.                             HLock(h);
  1025.                             BlockMove(toolName, *h, GetHandleSize(h));
  1026.                             HUnlock(h);
  1027.                             ChangedResource(h);
  1028.                         }
  1029.                         ReleaseResource(h);
  1030.                         
  1031.                         /* write the prefered configuration to the preference file */
  1032.                         p = CMGetConfig(docConn);
  1033.     
  1034.     
  1035.                         /* get the configuration TeXT resource */
  1036.                         h = Get1Resource('cTXT', 0);
  1037.                         if (h == nil)
  1038.                         {
  1039.                             /* resource did not exist, add it to resource file */
  1040.                             h = NewHandle(GetPtrSize(p));
  1041.                             HLock(h);
  1042.                             BlockMove(p, *h, GetHandleSize(h));
  1043.                             HUnlock(h);
  1044.                         
  1045.                             AddResource(h, 'cTXT', 0, "");
  1046.                             iErr = ResError();
  1047.                         }
  1048.                         else
  1049.                         {
  1050.                             /* resoure DOES exist, change it */
  1051.                             hSize = GetHandleSize(h);            /* get the size of the handle */
  1052.                             /* check for size of handle */
  1053.                             if (hSize != GetPtrSize(p))
  1054.                             {
  1055.                                 newSize = GetPtrSize(p);
  1056.                                 SetHandleSize(h, newSize);
  1057.                             }
  1058.                             HLock(h);
  1059.                             BlockMove(p, *h, GetHandleSize(h));
  1060.                             HUnlock(h);
  1061.                             ChangedResource(h);
  1062.                         }
  1063.                         ReleaseResource(h);
  1064.     
  1065.                         CloseResFile(prefRefNum);
  1066.                         iErr = ResError();
  1067.                         DisposPtr(p);
  1068.                     }
  1069.                 }
  1070.             }
  1071.             
  1072.             if (docConn != nil)
  1073.             {
  1074.                 /* dispose of the connection */
  1075.                 CMDispose(docConn);
  1076.             }
  1077.     
  1078.         }
  1079.     }
  1080.     else
  1081.     {
  1082.         SysBeep(1);
  1083.         SysBeep(1);
  1084.     }
  1085. }
  1086.  
  1087.  
  1088. void    DoCommPortSetup(prefsFileName)
  1089. Str255    prefsFileName;
  1090. {
  1091.     setCTBpref(prefsFileName);
  1092.  
  1093. }
  1094.  
  1095. - -----------------------------------------------------
  1096. Mark Sproul - KB2ICI - New Jersey
  1097. sproul@sproul.com
  1098.  
  1099. +++++++++++++++++++++++++++
  1100.  
  1101. From: grobbins@Apple.COM (Grobbins)
  1102. Date: 7 Jan 93 09:42:05 GMT
  1103. Organization: Mac Experimental System Software
  1104.  
  1105. In article <D2150096.mv05jr@sproul.sproul.com> Sproul@sproul.sproul.com (Mark Sproul) writes:
  1106. >Below is code for finding and working with prefs file in system 7
  1107. >prefernce folder and working with said file
  1108.  
  1109. Modern Mac applications should use the FindFolder call to locate
  1110. the Preferences folder.  For an example, see the DTS snippet prefs 
  1111. (available via anonymous ftp from ftp.apple.com, in the directory 
  1112. /dts/mac/sc/snippets/toolbox.)  FindFolder is documented in the 
  1113. Finder Interface chapter of Inside Mac VI.
  1114.  
  1115. Grobbins           grobbins@apple.com
  1116.  
  1117. No disclaimers apply.
  1118.  
  1119. +++++++++++++++++++++++++++
  1120.  
  1121. From: neeri@iis.ethz.ch (Matthias Neeracher)
  1122. Date: 7 Jan 93 22:41:43 GMT
  1123. Organization: Integrated Systems Laboratory, ETH, Zurich
  1124.  
  1125. In article <D2150096.mv05jr@sproul.sproul.com>, Sproul@sproul.sproul.com (Mark Sproul) writes:
  1126.  
  1127. > Below is code for finding and working with prefs file in system 7
  1128. > prefernce folder and working with said file
  1129.  
  1130. Why is this code better than FindFolder() ?
  1131.          
  1132. >         /* check for the preferences folder */
  1133. >         (*infoPB).hFileInfo.ioFDirIndex    = 0;
  1134. >         (*infoPB).hFileInfo.ioVRefNum    = prefVRefNum;
  1135. >         (*infoPB).hFileInfo.ioDirID        = sysfDirID;
  1136. >         (*infoPB).hFileInfo.ioNamePtr    = "\pPreferences";
  1137.  
  1138. >From SysTypes.r:
  1139.  
  1140. #define Language    langEnglish, langFrench, langGerman, langItalian,
  1141. langDutch, langSwedish, langSpanish, langDanish,    \
  1142. langPortuguese, langNorwegian, langHebrew,            \
  1143. langJapanese, langArabic, langFinnish, langGreek,    \
  1144. langIcelandic, langMaltese, langTurkish,            \
  1145. langCroatian, langTradChinese, langUrdu,            \
  1146. langHindi, langThai, langKorean, langLithuanian,    \
  1147. langPolish, langHungarian, langEstonian,            \
  1148. langLettish, langLappish, langFaeroese,                \
  1149. langFarsi, langRussian, langSimpChinese,            \
  1150. langFlemish, langIrish, langAlbanian, langRomanian,    \
  1151. langCzech, langSlovak, langSlovenian, langYiddish,    \
  1152. langSerbian, langMacedonian, langBulgarian,            \
  1153. langUkrainian, langByelorussian, langUzbek,            \
  1154. langKazakh, langAzerbaijani, langAzerbaijanAr,        \
  1155. langArmenian, langGeorgian, langMoldavian,            \
  1156. langKirghiz, langTajiki, langTurkmen,                \
  1157. langMongolian, langMongolianCyr, langPashto,        \
  1158. langKurdish, langKashmiri, langSindhi, langTibetan,    \
  1159. langNepali, langSanskrit, langMarathi, langBengali,    \
  1160. langAssamese, langGujarati, langPunjabi, langOriya,    \
  1161. langMalayalam, langKannada, langTamil, langTelugu,    \
  1162. langSinhalese, langBurmese, langKhmer, langLao,        \
  1163. langVietnamese, langIndonesian, langTagalog,        \
  1164. langMalayRoman, langMalayArabic, langAmharic,        \
  1165. langTigrinya, langGalla, langSomali, langSwahili,    \
  1166. langRuanda, langRundi, langChewa, langMalagasy,        \
  1167. langEsperanto, langUnspecified = 32767                \
  1168.  
  1169. Are you sure that the speakers of all of these languages will be happy
  1170. with your hardcoding the name "Preferences" ?
  1171.  
  1172. Matthias
  1173.  
  1174. - -----
  1175. Matthias Neeracher                                   neeri@iis.ethz.ch
  1176.    "There once was an Age of Reason, but we've progressed beyond it."
  1177.                                    -- Ayn Rand, _Atlas Shrugged_
  1178.  
  1179. +++++++++++++++++++++++++++
  1180.  
  1181. From: jesjones@stein.u.washington.edu (Jesse Jones)
  1182. Date: 9 Jan 1993 05:28:21 GMT
  1183. Organization: University of Washington, Seattle
  1184.  
  1185.  
  1186.    There has been some discussion lately about how to handle a preferences
  1187. file, so I thought I'd post my code. It's nothing astounding, but I think
  1188. it has some advantages over the previously posted code:
  1189.  
  1190.    1) FindFolder and the high level file manager routines are used so
  1191.       the code is quit a bit easier to follow.
  1192.    2) The GetPref procedure is passed a procedure parameter that is used to
  1193.       return a default preference if the preference is missing from the
  1194.       Prefs file.
  1195.    3) Individual preferences are referenced by the ResType, resource name,
  1196.       and version number. If the version numbers don't match the default
  1197.       preference is returned.
  1198.       
  1199.  
  1200.    --Jesse
  1201.    
  1202.    
  1203.    Here is the interface for the Prefs module. It's written in SemperSoft
  1204. Modula-2. HANDLE is compatible with any pointer to pointer and Mstring is
  1205. a 256-byte string terminated with zero.
  1206.  
  1207. DEFINITION MODULE Prefs;   (* By Jesse Jones, 1992 *)
  1208.  
  1209.  
  1210. FROM SYSTEM     IMPORT HANDLE;
  1211. FROM Types      IMPORT Str255, Mstring;
  1212.  
  1213.  
  1214. TYPE
  1215.    PrefsProc = PROCEDURE(): HANDLE;
  1216.    
  1217. VAR
  1218.    PrefsName: Str255;         (* Name of the Preferences file. *)
  1219.  
  1220.  
  1221. PROCEDURE GetPref (rType, rName: Mstring; vers: INTEGER; default: PrefsProc): HANDLE;
  1222.    (* default is called if Prefs not present or version <> vers. *)
  1223.  
  1224. PROCEDURE SetPref (rType, rName: Mstring; vers: INTEGER; data: HANDLE);
  1225.    (* Change or add a preference. *)
  1226.  
  1227. PROCEDURE PrefPresent (rType, rName: Mstring; vers: INTEGER): BOOLEAN;
  1228.  
  1229. PROCEDURE DeletePref (rType, rName: Mstring);
  1230.  
  1231.  
  1232. END Prefs.
  1233.  
  1234.    And here is the implementation of the Prefs module.
  1235.  
  1236. IMPLEMENTATION MODULE Prefs;
  1237.  
  1238. (* Import list deleted *)
  1239.  
  1240.    (* Version numbers are saved in PVRS resources. These resources contain a
  1241.       single integer for the version number and are referenced by name, with
  1242.       the name being the concatenation of the preference resource type and
  1243.       the preference's name. *)
  1244. PROCEDURE GetVersion (rType, rName: Mstring): INTEGER;
  1245.    VAR 
  1246.       data: IntHandle;
  1247.       str : Str255;
  1248.       vers: INTEGER;
  1249.       err : OSErr;
  1250. BEGIN
  1251.    str := StrToPStr(Concat(rType, rName));
  1252.    data := Get1NamedResource('PVRS', str);
  1253.    err := ResError();
  1254.    IF (err = resNotFound) OR (data = NIL) THEN
  1255.       vers := -1;
  1256.    ELSIF ErrCheck(err) THEN
  1257.       vers := data^^;
  1258.    ELSE
  1259.       vers := -1;
  1260.    END;
  1261.    RETURN vers;
  1262. END GetVersion;
  1263.  
  1264. PROCEDURE SetVersion (rType, rName: Mstring; version: INTEGER);
  1265.    VAR 
  1266.       data : IntHandle;
  1267.       str  : Mstring;
  1268.       err  : OSErr;
  1269.       dummy: BOOLEAN;
  1270. BEGIN
  1271.    str := Concat(rType, rName);
  1272.    data := Get1NamedResource('PVRS', StrToPStr(str));
  1273.    err := ResError();
  1274.    IF (err = resNotFound) OR (data = NIL) THEN
  1275.       data := CreateHandle(SIZE(INTEGER));
  1276.       data^^ := version;
  1277.       dummy := InsertResource('PVRS', str, 0, data, TRUE);
  1278.    ELSIF ErrCheck(err) THEN
  1279.       data^^ := version;
  1280.       dummy := ChangeResource(data);
  1281.    END;
  1282. END SetVersion;
  1283.  
  1284. PROCEDURE OpenPrefs (VAR refNum: INTEGER; perm: SignedByte): BOOLEAN;
  1285.    VAR
  1286.       vRef : INTEGER;
  1287.       dir  : LONGINT;
  1288.       err  : OSErr;
  1289.       dummy: BOOLEAN;
  1290. BEGIN
  1291.    err := FindPrefsFolder(vRef, dir);                           (* find (or create) prefs folder *)
  1292.    IF ErrCheck(err) THEN
  1293.       refNum := HOpenResFile(vRef, dir, PrefsName, perm);      (* open prefs file *)
  1294.       err := ResError();
  1295.       IF err = fnfErr THEN                                       (* if prefs file is missing *)
  1296.          HCreateResFile(vRef, dir, PrefsName);                  (* then create it *)
  1297.          refNum := HOpenResFile(vRef, dir, PrefsName, perm);   (* and open the new file *)
  1298.          err := ResError();
  1299.          dummy := ErrCheck(err);
  1300.       END;
  1301.    END;
  1302.    RETURN err = noErr;
  1303. END OpenPrefs;
  1304.  
  1305. PROCEDURE GetPref (rType, rName: Mstring; vers: INTEGER; default: PrefsProc): HANDLE;
  1306.    VAR
  1307.       refNum: INTEGER;
  1308.       data  : HANDLE;
  1309.       err   : OSErr;
  1310. BEGIN
  1311.    IF OpenPrefs(refNum, fsRdPerm) THEN
  1312.       IF GetVersion(rType, rName) <> vers THEN
  1313.          data := default();
  1314.       ELSE
  1315.          data := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
  1316.          err := ResError();
  1317.          IF (err = resNotFound) OR (data = NIL) THEN
  1318.             data := default();
  1319.          ELSIF ErrCheck(err) THEN
  1320.             DetachResource(data);
  1321.             HNoPurge(data);
  1322.          ELSE
  1323.             data := default();
  1324.          END;
  1325.       END;
  1326.       CloseResFile(refNum);
  1327.    ELSE
  1328.       data := default();
  1329.    END;
  1330.    Assert(data <> NIL);
  1331.    RETURN data;
  1332. END GetPref;
  1333.  
  1334. PROCEDURE PrefPresent (rType, rName: Mstring; vers: INTEGER): BOOLEAN;
  1335.    VAR
  1336.       refNum: INTEGER;
  1337.       data  : HANDLE;
  1338.       found : BOOLEAN;
  1339.       err   : OSErr;
  1340. BEGIN
  1341.    found := FALSE;
  1342.    IF OpenPrefs(refNum, fsRdPerm) THEN
  1343.       IF GetVersion(rType, rName) >= vers THEN
  1344.          data := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
  1345.          err := ResError();
  1346.          IF (err <> resNotFound) AND (data <> NIL) THEN
  1347.             found := ErrCheck(err);
  1348.          END;
  1349.       END;
  1350.       CloseResFile(refNum);
  1351.    END;
  1352.    RETURN found;
  1353. END PrefPresent;
  1354.  
  1355.    (* SetPref is the only routine to actually add a preference to the
  1356.       Prefs file. Note that the preferences are purgable. *)
  1357. PROCEDURE SetPref (rType, rName: Mstring; vers: INTEGER; data: HANDLE);
  1358.    VAR
  1359.       refNum: INTEGER;
  1360.       old   : HANDLE;
  1361.       err   : OSErr;
  1362.       done  : BOOLEAN;
  1363. BEGIN
  1364.    done := FALSE;
  1365.    Assert(data <> NIL);
  1366.    IF OpenPrefs(refNum, fsRdWrPerm) THEN
  1367.       old := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
  1368.       err := ResError();
  1369.       IF (err = resNotFound) OR (old = NIL) THEN
  1370.          done := InsertResource(rType, rName, 0, data, TRUE);   
  1371.       ELSIF ErrCheck(err) THEN
  1372.          RemoveResource(old);
  1373.          done := InsertResource(rType, rName, 0, data, TRUE);
  1374.       END;
  1375.       IF done THEN 
  1376.          SetVersion(rType, rName, vers);
  1377.          DetachResource(data);
  1378.       END;
  1379.       CloseResFile(refNum);
  1380.    END;
  1381. END SetPref;
  1382.  
  1383. PROCEDURE DeletePref (rType, rName: Mstring);
  1384.    VAR
  1385.       refNum: INTEGER;
  1386.       old   : HANDLE;
  1387.       err   : OSErr;
  1388. BEGIN
  1389.    IF OpenPrefs(refNum, fsRdWrPerm) THEN
  1390.       old := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
  1391.       err := ResError();
  1392.       IF (err = resNotFound) OR (old = NIL) THEN
  1393.          (* do nothing *)
  1394.       ELSIF ErrCheck(err) THEN
  1395.          RemoveResource(old);
  1396.       END;
  1397.       CloseResFile(refNum);
  1398.    END;
  1399. END DeletePref;
  1400.  
  1401.  
  1402. BEGIN
  1403.    PrefsName := "";
  1404. END Prefs.
  1405.  
  1406.    InsertResource, ChangeResource, and RemoveResource are some handy resource
  1407. manipulation routines I wrote a while back. I'll post them in a seperate
  1408. message.
  1409.  
  1410.  
  1411. +++++++++++++++++++++++++++
  1412.  
  1413. From: jesjones@stein.u.washington.edu (Jesse Jones)
  1414. Date: 10 Jan 93 01:55:19 GMT
  1415. Organization: University of Washington, Seattle
  1416.  
  1417.  
  1418.   Here are some handy routines for dealing with resources. Several of these
  1419. routines are used by the Prefs code I posted recently. A few of these routines
  1420. replace Apple's standard routines with something that behaves a little better.
  1421.  
  1422.    --Jesse
  1423.    
  1424.    
  1425. PROCEDURE Unique (theType: ResType): INTEGER; 
  1426.     INLINE2(0A9C1H); 
  1427.  
  1428.    (* The toolbox trap, UniqueID, returns an unused positive resource ID
  1429.       number. However, application resources need to be larger than 127 to
  1430.       avoid conflicts with other resources used by the System. The below 
  1431.       procedure ensures that the ID is  in the correct range. *)
  1432. PROCEDURE UniqueID (theType: ResType; system: BOOLEAN): INTEGER; 
  1433.    VAR 
  1434.       ID  : INTEGER;
  1435.       done: BOOLEAN;
  1436. BEGIN
  1437.    REPEAT
  1438.       ID := Unique(theType);      (* always returns positive IDs *)
  1439.       IF system THEN
  1440.          done := ID < 128;
  1441.       ELSE
  1442.          done := ID > 127;
  1443.       END;
  1444.    UNTIL done;
  1445.    RETURN ID;
  1446. END UniqueID;
  1447.  
  1448. PROCEDURE Unique1 (theType: ResType): INTEGER; 
  1449.     INLINE2(0A810H); 
  1450.  
  1451. PROCEDURE Unique1ID (theType: ResType; system: BOOLEAN): INTEGER; 
  1452.    VAR 
  1453.       ID  : INTEGER;
  1454.       done: BOOLEAN;
  1455. BEGIN
  1456.    REPEAT
  1457.       ID := Unique1(theType);      (* always returns positive IDs *)
  1458.       IF system THEN
  1459.          done := ID < 128;
  1460.       ELSE
  1461.          done := ID > 127;
  1462.       END;
  1463.    UNTIL done;
  1464.    RETURN ID;
  1465. END Unique1ID;
  1466.  
  1467. PROCEDURE GetResourceName (rsrc: HANDLE): Mstring;
  1468.    VAR
  1469.       rID  : INTEGER;
  1470.       kind : ResType;
  1471.       rName: Str255;
  1472. BEGIN
  1473.    GetResInfo(rsrc, rID, kind, rName);
  1474.    RETURN PStrToStr(rName);
  1475. END GetResourceName;
  1476.  
  1477. PROCEDURE GetResourceID (rsrc: HANDLE): INTEGER;
  1478.    VAR
  1479.       rID  : INTEGER;
  1480.       kind : ResType;
  1481.       rName: Str255;
  1482. BEGIN
  1483.    GetResInfo(rsrc, rID, kind, rName);
  1484.    RETURN rID;
  1485. END GetResourceID;
  1486.  
  1487.    (* resources can be specified using either name, or ID (if name is ""). *)
  1488. PROCEDURE InsertResource (type, name: Mstring; ID: INTEGER; rsrc: HANDLE; purgeable: BOOLEAN): BOOLEAN;
  1489.    VAR
  1490.       rName : Str255;
  1491.       rType : ResType;
  1492.       refNum: INTEGER;
  1493.       done  : BOOLEAN;
  1494. BEGIN
  1495.    done := FALSE;
  1496.    rType := StrToOS(type);
  1497.    IF name[0] = EOS THEN
  1498.       rName := "";                     
  1499.    ELSE
  1500.       rName := StrToPStr(name);         
  1501.       ID := Unique1ID(rType, FALSE);   
  1502.    END;
  1503.    AddResource(rsrc, rType, ID, rName);
  1504.    IF ErrCheck(ResError()) THEN
  1505.       IF purgeable THEN SetResAttrs(rsrc, resPurgeable) END;   (* clears resChanged bit! *)
  1506.       IF ErrCheck(ResError()) THEN
  1507.          ChangedResource(rsrc);
  1508.          IF ErrCheck(ResError()) THEN
  1509.             refNum := CurResFile();
  1510.             UpdateResFile(refNum);
  1511.             done := ErrCheck(ResError());
  1512.          END;
  1513.       END;
  1514.    END;    
  1515.    RETURN done;
  1516. END InsertResource;
  1517.    
  1518. PROCEDURE ChangeResource (rsrc: HANDLE): BOOLEAN;
  1519.    VAR
  1520.       refNum: INTEGER;
  1521.       done  : BOOLEAN;
  1522. BEGIN
  1523.    done := FALSE;
  1524.    ChangedResource(rsrc);
  1525.    IF ErrCheck(ResError()) THEN
  1526.       refNum := CurResFile();
  1527.       UpdateResFile(refNum);
  1528.       done := ErrCheck(ResError());
  1529.    END;
  1530.    RETURN done;
  1531. END ChangeResource;
  1532.    
  1533. PROCEDURE RemoveResource (rsrc: HANDLE);
  1534.    VAR
  1535.       refNum: INTEGER;
  1536.       done  : BOOLEAN;
  1537. BEGIN
  1538.    RmveResource(rsrc);
  1539.    IF ErrCheck(ResError()) THEN
  1540.       refNum := CurResFile();
  1541.       UpdateResFile(refNum);
  1542.       KillHandle(rsrc);
  1543.       done := ErrCheck(ResError());
  1544.    END;    
  1545. END RemoveResource;
  1546.  
  1547. ---------------------------
  1548.  
  1549. End of C.S.M.P. Digest
  1550. **********************
  1551.